home *** CD-ROM | disk | FTP | other *** search
-
- {*******************************************************}
- { }
- { Delphi Visual Component Library }
- { Client DataSet }
- { }
- { Copyright (c) 1997 Borland International }
- { }
- {*******************************************************}
-
- unit DBClient;
-
- {$R-}
-
- interface
-
- uses Windows, SysUtils, Graphics, Classes, Controls, Forms, Db,
- BDE, DSIntf, DBCommon, StdVcl;
-
- type
-
- { Exceptions }
-
- EDBClient = class(EDatabaseError)
- FErrorCode: DBIResult;
- public
- constructor Create(Message: string; ErrorCode: DBIResult);
- property ErrorCode: DBIResult read FErrorCode;
- end;
-
- EReconcileError = class(EDatabaseError)
- FContext: string;
- public
- constructor Create(NativeError, Context: string; ErrorCode: DBIResult);
- property Context: string read FContext;
- end;
-
- { TRemoteServer }
-
- TClientDataSet = class;
-
- TCustomRemoteServer = class(TComponent)
- protected
- procedure AddDataSet(DataSet: TClientDataSet); virtual;
- procedure RemoveDataSet(DataSet: TClientDataSet); virtual;
- function GetConnected: Boolean; virtual;
- function GetProvider(const ProviderName: string): IProvider; virtual; abstract;
- procedure SetConnected(Value: Boolean); virtual; abstract;
- property Connected: Boolean read GetConnected write SetConnected default False;
- end;
-
- TRemoteServer = class(TCustomRemoteServer)
- private
- FComputerName: string;
- FServerName: string;
- FDataSets: TList;
- FDispatch: IDispatch;
- FStreamedConnected: Boolean;
- FOnConnect: TNotifyEvent;
- FOnDisconnect: TNotifyEvent;
- procedure SetComputerName(const Value: string);
- procedure SetServerName(const Value: string);
- protected
- procedure AddDataSet(DataSet: TClientDataSet); override;
- function GetConnected: Boolean; override;
- procedure Loaded; override;
- procedure SetConnected(Value: Boolean); override;
- procedure RemoveDataSet(DataSet: TClientDataSet); override;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- function GetProvider(const ProviderName: string): IProvider; override;
- property ServerDispatch: IDispatch read FDispatch;
- published
- property ComputerName: string read FComputerName write SetComputerName;
- property Connected;
- property ServerName: string read FServerName write SetServerName;
- property OnConnect: TNotifyEvent read FOnConnect write FOnConnect;
- property OnDisconnect: TNotifyEvent read FOnDisconnect write FOnDisconnect;
- end;
-
- { TClientDataSet }
-
- PFieldDescList = ^TFieldDescList;
- TFieldDescList = array[0..1023] of DSFLDDesc;
-
- TKeyIndex = (kiLookup, kiRangeStart, kiRangeEnd, kiCurRangeStart,
- kiCurRangeEnd, kiSave);
-
- PRecInfo = ^TRecInfo;
- TRecInfo = packed record
- RecordNumber: Longint;
- BookmarkFlag: TBookmarkFlag;
- Attribute: DSAttr;
- end;
-
- PKeyBuffer = ^TKeyBuffer;
- TKeyBuffer = record
- Modified: Boolean;
- Exclusive: Boolean;
- FieldCount: Integer;
- Data: record end;
- end;
-
- TReconcileAction = (raSkip, raAbort, raMerge, raCorrect, raCancel, raRefresh);
- TReconcileErrorEvent = procedure(DataSet: TClientDataSet; E: EReconcileError;
- UpdateKind: TUpdateKind; var Action: TReconcileAction) of object;
-
- TClientDataSet = class(TDataSet)
- private
- FDSBase: IDSBase;
- FDSCursor: IDSCursor;
- FLookupCursor: IDSCursor;
- FFindCursor: IDSCursor;
- FCloneSource: TClientDataSet;
- FData: OleVariant;
- FDelta: OleVariant;
- FIndexDefs: TIndexDefs;
- FIndexName: string;
- FExprFilter: HDSFilter;
- FFuncFilter: HDSFilter;
- FFilterBuffer: PChar;
- FMasterLink: TMasterDataLink;
- FIndexFieldMap: DSKEY;
- FKeyBuffers: array[TKeyIndex] of PKeyBuffer;
- FKeyBuffer: PKeyBuffer;
- FNewValueBuffer: PChar;
- FOldValueBuffer: PChar;
- FCurValueBuffer: PChar;
- FIndexFieldCount: Integer;
- FProvider: IProvider;
- FProviderName: string;
- FRemoteServer: TCustomRemoteServer;
- FPacketRecords: Integer;
- FConstDisableCount: Integer;
- FKeySize: Word;
- FRecordSize: Word;
- FBookmarkOfs: Word;
- FRecInfoOfs: Word;
- FRecBufSize: Word;
- FReadOnly: Boolean;
- FFieldsIndex: Boolean;
- FCanModify: Boolean;
- FInReconcileCallback: Boolean;
- FNotifyCallback: Boolean;
- FProviderEOF: Boolean;
- FFetchOnDemand: Boolean;
- FOnReconcileError: TReconcileErrorEvent;
- procedure AddExprFilter(const Expr: string; Options: TFilterOptions);
- procedure AddFuncFilter;
- function CalcFieldsCallBack(RecBuf: PChar): DBIResult; stdcall;
- procedure CheckMasterRange;
- procedure CheckProviderEOF;
- function CreateDSBase: IDSBase;
- function CreateDSCursor(SourceCursor: IDSCursor): IDSCursor;
- procedure DecodeIndexDesc(const IndexDesc: DSIDXDesc;
- var Name, Fields: string; var Options: TIndexOptions);
- procedure EncodeFieldDesc(var FieldDesc: DSFLDDesc; const Name: string;
- DataType: TFieldType; Size: Word; Calculated: Boolean);
- procedure EncodeIndexDesc(var IndexDesc: DSIDXDesc;
- const Name, Fields: string; Options: TIndexOptions);
- procedure FetchMoreData(All: Boolean);
- function FilterCallback(RecBuf: PChar): Bool; stdcall;
- function GetActiveRecBuf(var RecBuf: PChar): Boolean;
- function GetChangeCount: Integer;
- function GetData: OleVariant;
- function GetDelta: OleVariant;
- function GetIndexDefs: TIndexDefs;
- function GetIndexFieldNames: string;
- function GetIndexName: string;
- function GetLogChanges: Boolean;
- function GetMasterFields: string;
- function GetProvider: IProvider;
- procedure InitBufferPointers(GetProps: Boolean);
- procedure MasterChanged(Sender: TObject);
- procedure MasterDisabled(Sender: TObject);
- procedure NotifyCallback; stdcall;
- procedure ReadData(Stream: TStream);
- function ReconcileCallback(iRslt: Integer; iUpdateKind: DSAttr;
- iResAction: dsCBRType; iErrCode: Integer; pErrMessage, pErrContext: PChar;
- pRecUpd, pRecOrg, pRecConflict: Pointer): dsCBRType; stdcall;
- procedure SetData(Value: OleVariant);
- procedure SetDataSource(Value: TDataSource);
- procedure SetIndex(const Value: string; FieldsIndex: Boolean);
- procedure SetIndexFieldNames(const Value: string);
- procedure SetIndexName(const Value: string);
- procedure SetLogChanges(Value: Boolean);
- procedure SetMasterFields(const Value: string);
- procedure SetNotifyCallback;
- procedure SetProvider(Value: IProvider);
- procedure SetProviderName(const Value: string);
- procedure SetRemoteServer(Value: TCustomRemoteServer);
- procedure SortOnFields(Cursor: IDSCursor; const Fields: string;
- CaseInsensitive, Descending: Boolean);
- procedure SetupInternalCalcFields;
- procedure WriteData(Stream: TStream);
- protected
- procedure ActivateFilters;
- procedure AddDataPacket(Data: OleVariant; HitEOF: Boolean); virtual;
- procedure AddFieldDesc(FieldDesc: DSFLDDesc; Required: Boolean; FieldNo: Word);
- procedure AllocKeyBuffers;
- function AllocRecordBuffer: PChar; override;
- function BCDToCurr(BCD: Pointer; var Curr: Currency): Boolean; override;
- function CurrToBCD(const Curr: Currency; BCD: Pointer; Precision,
- Decimals: Integer): Boolean; override;
- procedure Cancel; override;
- procedure Check(Status: DBIResult);
- procedure CheckSetKeyMode;
- procedure ClearCalcFields(Buffer: PChar); override;
- procedure CloseCursor; override;
- function CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream; override;
- procedure DeactivateFilters;
- procedure DefineProperties(Filer: TFiler); override;
- procedure DestroyLookupCursor; virtual;
- procedure DoOnNewRecord; override;
- function FindRecord(Restart, GoForward: Boolean): Boolean; override;
- procedure FreeKeyBuffers;
- procedure FreeRecordBuffer(var Buffer: PChar); override;
- procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
- function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
- function GetCanModify: Boolean; override;
- function GetDataSource: TDataSource; override;
- function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
- function GetIndexField(Index: Integer): TField;
- function GetIndexFieldCount: Integer;
- function GetIsIndexField(Field: TField): Boolean; override;
- function GetKeyBuffer(KeyIndex: TKeyIndex): PKeyBuffer;
- function GetKeyExclusive: Boolean;
- function GetKeyFieldCount: Integer;
- function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
- function GetRecordCount: Integer; override;
- function GetRecNo: Integer; override;
- function GetRecordSize: Word; override;
- function GetStateFieldValue(State: TDataSetState; Field: TField): Variant; override;
- function InitKeyBuffer(Buffer: PKeyBuffer): PKeyBuffer;
- procedure InitRecord(Buffer: PChar); override;
- procedure InternalAddRecord(Buffer: Pointer; Append: Boolean); override;
- procedure InternalClose; override;
- procedure InternalDelete; override;
- procedure InternalFirst; override;
- procedure InternalGotoBookmark(Bookmark: TBookmark); override;
- procedure InternalHandleException; override;
- procedure InternalInitFieldDefs; override;
- procedure InternalInitRecord(Buffer: PChar); override;
- procedure InternalLast; override;
- procedure InternalOpen; override;
- procedure InternalPost; override;
- procedure InternalSetToRecord(Buffer: PChar); override;
- function IsCursorOpen: Boolean; override;
- procedure Loaded; override;
- function LocateRecord(const KeyFields: string; const KeyValues: Variant;
- Options: TLocateOptions; SyncCursor: Boolean): Boolean;
- procedure OpenCursor(InfoQuery: Boolean); override;
- procedure Notification(AComponent: TComponent; Operation: TOperation); override;
- procedure Post; override;
- procedure PostKeyBuffer(Commit: Boolean);
- procedure RefreshInternalCalcFields(Buffer: PChar); override;
- function ResetCursorRange: Boolean;
- procedure SetAltRecBuffers(Old, New, Cur: PChar);
- procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
- procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
- function SetCursorRange: Boolean;
- procedure SetFieldData(Field: TField; Buffer: Pointer); override;
- procedure SetFilterData(const Text: string; Options: TFilterOptions);
- procedure SetFiltered(Value: Boolean); override;
- procedure SetFilterOptions(Value: TFilterOptions); override;
- procedure SetFilterText(const Value: string); override;
- procedure SetIndexField(Index: Integer; Value: TField);
- procedure SetKeyBuffer(KeyIndex: TKeyIndex; Clear: Boolean);
- procedure SetKeyExclusive(Value: Boolean);
- procedure SetKeyFieldCount(Value: Integer);
- procedure SetKeyFields(KeyIndex: TKeyIndex; const Values: array of const);
- procedure SetLinkRanges(MasterFields: TList);
- procedure SetOnFilterRecord(const Value: TFilterRecordEvent); override;
- procedure SetRecNo(Value: Integer); override;
- procedure SwitchToIndex(const IndexName: string);
- procedure SyncCursors(Cursor1, Cursor2: IDSCursor);
- property DSBase: IDSBase read FDSBase;
- property DSCursor: IDSCursor read FDSCursor;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- procedure AddIndex(const Name, Fields: string; Options: TIndexOptions);
- procedure AppendData(Data: OleVariant; HitEOF: Boolean);
- procedure ApplyRange;
- function ApplyUpdates(MaxErrors: Integer): Integer;
- function BookmarkValid(Bookmark: TBookmark): Boolean; override;
- procedure CancelRange;
- procedure CancelUpdates;
- procedure CreateDataSet;
- procedure CloneCursor(Source: TClientDataSet; Reset: Boolean);
- procedure ClearChangeLog;
- function CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer; override;
- procedure DeleteIndex(const Name: string);
- procedure DisableConstraints;
- procedure EnableConstraints;
- procedure EditKey;
- procedure EditRangeEnd;
- procedure EditRangeStart;
- function FindKey(const KeyValues: array of const): Boolean;
- procedure FindNearest(const KeyValues: array of const);
- function GetCurrentRecord(Buffer: PChar): Boolean; override;
- procedure GetIndexInfo;
- procedure GetIndexNames(List: TStrings);
- function GetNextPacket: Integer;
- procedure GotoCurrent(DataSet: TClientDataSet);
- function GotoKey: Boolean;
- procedure GotoNearest;
- function Locate(const KeyFields: string; const KeyValues: Variant;
- Options: TLocateOptions): Boolean; override;
- function Lookup(const KeyFields: string; const KeyValues: Variant;
- const ResultFields: string): Variant; override;
- procedure LoadFromFile(const FileName: string);
- procedure LoadFromStream(Stream: TStream);
- function Reconcile(Results: OleVariant): Boolean;
- procedure RevertRecord;
- procedure SaveToFile(const FileName: string);
- procedure SaveToStream(Stream: TStream);
- procedure SetKey;
- procedure SetRange(const StartValues, EndValues: array of const);
- procedure SetRangeEnd;
- procedure SetRangeStart;
- function UndoLastChange(FollowChange: Boolean): Boolean;
- procedure UpdateIndexDefs; override;
- function UpdateStatus: TUpdateStatus;
- property ChangeCount: Integer read GetChangeCount;
- property Data: OleVariant read GetData write SetData;
- property Delta: OleVariant read GetDelta;
- property IndexDefs: TIndexDefs read GetIndexDefs;
- property IndexFieldCount: Integer read GetIndexFieldCount;
- property IndexFields[Index: Integer]: TField read GetIndexField write SetIndexField;
- property KeyExclusive: Boolean read GetKeyExclusive write SetKeyExclusive;
- property KeyFieldCount: Integer read GetKeyFieldCount write SetKeyFieldCount;
- property KeySize: Word read FKeySize;
- property LogChanges: Boolean read GetLogChanges write SetLogChanges;
- property Provider: IProvider read GetProvider write SetProvider;
- published
- property Active;
- property AutoCalcFields;
- property FetchOnDemand: Boolean read FFetchOnDemand write FFetchOnDemand default True;
- property Filter;
- property Filtered;
- property FilterOptions;
- property IndexFieldNames: string read GetIndexFieldNames write SetIndexFieldNames;
- property IndexName: string read GetIndexName write SetIndexName;
- property MasterFields: string read GetMasterFields write SetMasterFields;
- property MasterSource: TDataSource read GetDataSource write SetDataSource;
- property PacketRecords: Integer read FPacketRecords write FPacketRecords default -1;
- property ProviderName: string read FProviderName write SetProviderName;
- property ReadOnly: Boolean read FReadOnly write FReadOnly default False;
- property RemoteServer: TCustomRemoteServer read FRemoteServer write SetRemoteServer;
- property BeforeOpen;
- property AfterOpen;
- property BeforeClose;
- property AfterClose;
- property BeforeInsert;
- property AfterInsert;
- property BeforeEdit;
- property AfterEdit;
- property BeforePost;
- property AfterPost;
- property BeforeCancel;
- property AfterCancel;
- property BeforeDelete;
- property AfterDelete;
- property BeforeScroll;
- property AfterScroll;
- property OnCalcFields;
- property OnDeleteError;
- property OnEditError;
- property OnFilterRecord;
- property OnNewRecord;
- property OnPostError;
- property OnReconcileError: TReconcileErrorEvent read FOnReconcileError write FOnReconcileError;
- end;
-
- implementation
-
- uses DBConsts, ActiveX, ComObj;
-
- { Exceptions }
-
- constructor EDBClient.Create(Message: string; ErrorCode: DBIResult);
- begin
- FErrorCode := ErrorCode;
- inherited Create(Message);
- end;
-
- constructor EReconcileError.Create(NativeError, Context: string; ErrorCode: DBIResult);
- begin
- FContext := Context;
- inherited Create(NativeError);
- end;
-
- { Utility Routines }
-
- procedure CheckDataPacket(DataPacket: OleVariant);
- begin
- if not (VarIsArray(DataPacket) and (VarArrayHighBound(DataPacket, 1) > 20)) then
- DatabaseError(SInvalidDataPacket);
- end;
-
- type
-
- { TDSBlobStream }
-
- TDSBlobStream = class(TMemoryStream)
- private
- FField: TBlobField;
- FDataSet: TClientDataSet;
- FBuffer: PChar;
- FFieldNo: Integer;
- FModified: Boolean;
- procedure ReadBlobData;
- public
- constructor Create(Field: TBlobField; Mode: TBlobStreamMode);
- destructor Destroy; override;
- function Write(const Buffer; Count: Longint): Longint; override;
- procedure Truncate;
- end;
-
-
- constructor TDSBlobStream.Create(Field: TBlobField; Mode: TBlobStreamMode);
- begin
- FField := Field;
- FFieldNo := FField.FieldNo;
- FDataSet := FField.DataSet as TClientDataSet;
- if not FDataSet.GetActiveRecBuf(FBuffer) then Exit;
- if Mode <> bmRead then
- begin
- if FField.ReadOnly then DatabaseErrorFmt(SFieldReadOnly, [FField.DisplayName]);
- if not (FDataSet.State in [dsEdit, dsInsert]) then DatabaseError(SNotEditing);
- end;
- if Mode = bmWrite then Truncate
- else ReadBlobData;
- end;
-
- destructor TDSBlobStream.Destroy;
- begin
- if FModified then
- try
- FDataSet.Check(FDataSet.FDSCursor.PutBlob(FBuffer, FFieldNo, 0, Memory, Size));
- FField.Modified := True;
- FDataSet.DataEvent(deFieldChange, Longint(FField));
- except
- Application.HandleException(Self);
- end;
- end;
-
- procedure TDSBlobStream.ReadBlobData;
- var
- BlobLen: Integer;
- begin
- FDataSet.Check(FDataSet.FDSCursor.GetBlobLen(FBuffer, FFieldNo, BlobLen));
- if BlobLen > 0 then
- begin
- Position := 0;
- SetSize(BlobLen);
- FDataSet.Check(FDataSet.FDSCursor.GetBlob(FBuffer, FFieldNo, 0, Memory, BlobLen));
- end;
- end;
-
- function TDSBlobStream.Write(const Buffer; Count: Longint): Longint;
- begin
- Result := inherited Write(Buffer, Count);
- FModified := True;
- end;
-
- procedure TDSBlobStream.Truncate;
- begin
- Clear;
- FModified := True;
- end;
-
- { TCustomRemoteServer }
-
- procedure TCustomRemoteServer.AddDataSet(DataSet: TClientDataSet);
- begin
- end;
-
- procedure TCustomRemoteServer.RemoveDataSet(DataSet: TClientDataSet);
- begin
- end;
-
- function TCustomRemoteServer.GetConnected: Boolean;
- begin
- Result := False;
- end;
-
- type
-
- { TDispatchProvider }
-
- TDispatchProvider = class(TInterfacedObject, IProvider)
- private
- FProvider: DProvider;
- { IDispatch }
- function GetTypeInfoCount(out Count: Integer): Integer; stdcall;
- function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): Integer; stdcall;
- function GetIDsOfNames(const IID: TGUID; Names: Pointer;
- NameCount, LocaleID: Integer; DispIDs: Pointer): Integer; stdcall;
- function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
- Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): Integer; stdcall;
- { IProvider }
- function Get_Data: OleVariant; safecall;
- function ApplyUpdates(Delta: OleVariant; MaxErrors: Integer; out ErrorCount: Integer): OleVariant; safecall;
- function GetMetaData: OleVariant; safecall;
- function GetRecords(Count: Integer; out RecsOut: Integer): OleVariant; safecall;
- function DataRequest(Input: OleVariant): OleVariant; safecall;
- function Get_Constraints: WordBool; safecall;
- procedure Set_Constraints(Value: WordBool); safecall;
- procedure Reset; safecall;
- public
- constructor Create(const Provider: DProvider);
- end;
-
- { TDispatchProvider }
-
- constructor TDispatchProvider.Create(const Provider: DProvider);
- begin
- FProvider := Provider;
- end;
-
- { TDispatchProvider.IDispatch }
-
- function TDispatchProvider.GetTypeInfoCount(out Count: Integer): Integer;
- begin
- Result := IDispatch(FProvider).GetTypeInfoCount(Count);
- end;
-
- function TDispatchProvider.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): Integer;
- begin
- Result := IDispatch(FProvider).GetTypeInfo(Index, LocaleID, TypeInfo);
- end;
-
- function TDispatchProvider.GetIDsOfNames(const IID: TGUID; Names: Pointer;
- NameCount, LocaleID: Integer; DispIDs: Pointer): Integer;
- begin
- Result := IDispatch(FProvider).GetIDsOfNames(IID, Names, NameCount, LocaleID, DispIDs);
- end;
-
- function TDispatchProvider.Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
- Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): Integer;
- begin
- Result := IDispatch(FProvider).Invoke(DispID, IID, LocaleID, Flags, Params,
- VarResult, ExcepInfo, ArgErr);
- end;
-
- { TDispatchProvider.IProvider }
-
- function TDispatchProvider.Get_Data: OleVariant;
- begin
- Result := FProvider.Data;
- end;
-
- function TDispatchProvider.ApplyUpdates(Delta: OleVariant; MaxErrors: Integer; out ErrorCount: Integer): OleVariant;
- begin
- Result := FProvider.ApplyUpdates(Delta, MaxErrors, ErrorCount);
- end;
-
- function TDispatchProvider.GetMetaData: OleVariant;
- begin
- Result := FProvider.GetMetaData;
- end;
-
- function TDispatchProvider.GetRecords(Count: Integer; out RecsOut: Integer): OleVariant;
- begin
- Result := FProvider.GetRecords(Count, RecsOut);
- end;
-
- function TDispatchProvider.DataRequest(Input: OleVariant): OleVariant;
- begin
- Result := FProvider.DataRequest(Input);
- end;
-
- function TDispatchProvider.Get_Constraints: WordBool;
- begin
- Result := FProvider.Constraints;
- end;
-
- procedure TDispatchProvider.Set_Constraints(Value: WordBool);
- begin
- FProvider.Constraints := Value;
- end;
-
- procedure TDispatchProvider.Reset;
- begin
- FProvider.Reset;
- end;
-
- { TRemoteServer }
-
- function GetProperty(Obj: IDispatch; const Name: string): OleVariant;
- var
- ID: Integer;
- WideName: WideString;
- DispParams: TDispParams;
- ExcepInfo: TExcepInfo;
- Status: Integer;
- begin
- WideName := Name;
- OleCheck(Obj.GetIDsOfNames(GUID_NULL, @WideName, 1, 0, @ID));
- FillChar(DispParams, SizeOf(DispParams), 0);
- FillChar(ExcepInfo, SizeOf(ExcepInfo), 0);
- Status := Obj.Invoke(ID, GUID_NULL, 0, DISPATCH_PROPERTYGET, DispParams,
- @Result, @ExcepInfo, nil);
- if Status <> 0 then DispatchInvokeError(Status, ExcepInfo);
- end;
-
- constructor TRemoteServer.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FDataSets := TList.Create;
- end;
-
- destructor TRemoteServer.Destroy;
- begin
- SetConnected(False);
- FDataSets.Free;
- inherited Destroy;
- end;
-
- procedure TRemoteServer.Loaded;
- begin
- inherited Loaded;
- try
- if FStreamedConnected then SetConnected(True);
- except
- if csDesigning in ComponentState then
- Application.HandleException(Self)
- else
- raise;
- end;
- end;
-
- procedure TRemoteServer.SetComputerName(const Value: string);
- begin
- if Value <> FComputerName then
- begin
- SetConnected(False);
- FComputerName := Value;
- end;
- end;
-
- procedure TRemoteServer.SetServerName(const Value: string);
- begin
- if Value <> FServerName then
- begin
- SetConnected(False);
- FServerName := Value;
- end;
- end;
-
- procedure TRemoteServer.AddDataSet(DataSet: TClientDataSet);
- begin
- FDataSets.Add(DataSet);
- end;
-
- procedure TRemoteServer.RemoveDataSet(DataSet: TClientDataSet);
- begin
- FDataSets.Remove(DataSet);
- end;
-
- function TRemoteServer.GetConnected: Boolean;
- begin
- Result := FDispatch <> nil;
- end;
-
- procedure TRemoteServer.SetConnected(Value: Boolean);
- type
- TCoCreateFunc = function(const clsid: TCLSID; unkOuter: IUnknown;
- dwClsCtx: Longint; ServerInfo: PCoServerInfo; dwCount: Longint;
- rgmqResults: PMultiQIArray): HResult; stdcall;
- var
- I: Integer;
- WideName: WideString;
- ServerInfo: TCoServerInfo;
- MultiQI: TMultiQI;
- IDispatchID: TGuid;
- Handle: HModule;
- CoCreateInstanceEx: TCoCreateFunc;
- begin
- if (csReading in ComponentState) and Value then
- FStreamedConnected := True else
- begin
- if Value = GetConnected then Exit;
- if Value then
- begin
- CoCreateInstanceEx := nil;
- if ComputerName <> '' then
- begin
- FillChar(ServerInfo, SizeOf(ServerInfo), #0);
- WideName := ComputerName;
- ServerInfo.pwszName := PWideChar(WideName);
- IDispatchID := IDispatch;
- MultiQI.IID := @IDispatchID;
- { Need to reference CoCreateInstanceEx dynamically to prevent
- load errors when running under Win95 w/o DCOM. }
- Handle := GetModuleHandle('OLE32.DLL');
- Win32Check(Handle > HINSTANCE_ERROR);
- CoCreateInstanceEx := GetProcAddress(Handle, 'CoCreateInstanceEx'); { Do not localize }
- end;
- if Assigned(CoCreateInstanceEx) then
- begin
- OleCheck(CoCreateInstanceEx(ProgIDToClassID(FServerName), nil,
- CLSCTX_LOCAL_SERVER or CLSCTX_REMOTE_SERVER or CLSCTX_INPROC_SERVER,
- @ServerInfo, 1, @MultiQI));
- OleCheck(MultiQI.hr);
- FDispatch := MultiQI.Itf as IDispatch;
- end else
- { If no machine name is specified or running under Win95 w/o DCOM,
- support local servers using CoCreateInstance }
- OleCheck(CoCreateInstance(ProgIDToClassID(FServerName), nil,
- CLSCTX_LOCAL_SERVER or CLSCTX_INPROC_SERVER, IDispatch, FDispatch));
- if Assigned(FOnConnect) then FOnConnect(Self);
- end else
- begin
- for I := 0 to FDataSets.Count - 1 do
- if Assigned(TClientDataSet(FDataSets[I]).FProvider) then
- TClientDataSet(FDataSets[I]).FProvider := nil;
- FDispatch := nil;
- if Assigned(FOnDisconnect) then FOnDisconnect(Self);
- end;
- end;
- end;
-
- function TRemoteServer.GetProvider(const ProviderName: string): IProvider;
- var
- ProviderDispatch: IDispatch;
- begin
- SetConnected(True);
- ProviderDispatch := IDispatch(GetProperty(FDispatch, ProviderName));
- if ProviderDispatch.QueryInterface(IProvider, Result) <> 0 then
- Result := TDispatchProvider.Create(DProvider(ProviderDispatch));
- end;
-
- { TClientDataSet }
-
- constructor TClientDataSet.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- FData := System.Null;
- FMasterLink := TMasterDataLink.Create(Self);
- FMasterLink.OnMasterChange := MasterChanged;
- FMasterLink.OnMasterDisable := MasterDisabled;
- FPacketRecords := -1;
- FFetchOnDemand := True;
- end;
-
- destructor TClientDataSet.Destroy;
- begin
- Close;
- inherited Destroy;
- FMasterLink.Free;
- if Assigned(FProvider) then
- FProvider := nil;
- FIndexDefs.Free;
- end;
-
- function TClientDataSet.CreateDSBase: IDSBase;
- var
- Status: HResult;
- begin
- Status := CoCreateInstance(CLSID_DSBase, nil, CLSCTX_INPROC_SERVER or
- CLSCTX_LOCAL_SERVER, IDSBase, Result);
- if Status = S_OK then Exit;
- if Status = REGDB_E_CLASSNOTREG then
- RegisterComServer('DBCLIENT.DLL') else
- OleCheck(Status);
- Result := CreateComObject(CLSID_DSBase) as IDSBase;
- end;
-
- function TClientDataSet.CreateDSCursor(SourceCursor: IDSCursor): IDSCursor;
- begin
- Result := CreateComObject(CLSID_DSCursor) as IDSCursor;
- if Assigned(SourceCursor) then
- Check(Result.CloneCursor(SourceCursor)) else
- Check(Result.InitCursor(FDSBase));
- end;
-
- procedure TClientDataSet.OpenCursor(InfoQuery: Boolean);
- var
- RecsOut: Integer;
- begin
- FProviderEOF := True;
- if not Assigned(FDSBase) then
- begin
- if VarIsNull(FData) then
- begin
- Provider.Reset;
- FData := FProvider.GetRecords(FPacketRecords, RecsOut);
- FProviderEOF := RecsOut <> FPacketRecords;
- end;
- if VarIsNull(FData) then DatabaseError(SNoDataProvider);
- begin
- FDSBase := CreateDSBase;
- Check(FDSBase.AppendData(TVarData(FData).VArray, FProviderEOF));
- end;
- end;
- inherited OpenCursor(InfoQuery);
- if Assigned(FCloneSource) and not FCloneSource.BOF then
- begin
- SyncCursors(FDSCursor, FCloneSource.FDSCursor);
- CursorPosChanged;
- Resync([]);
- end;
- end;
-
- procedure TClientDataSet.Check(Status: DBIResult);
- var
- ErrMsg: array[0..2048] of Char;
- begin
- if Status <> 0 then
- begin
- FDSBase.GetErrorString(Status, ErrMsg);
- raise EDBClient.Create(ErrMsg, Status);
- end;
- end;
-
- procedure TClientDataSet.CloseCursor;
- begin
- inherited CloseCursor;
- if Assigned(FProvider) then
- begin
- FData := NULL;
- if Assigned(FRemoteServer) and FRemoteServer.Connected and
- not (csDestroying in ComponentState) then
- begin
- FProvider.Reset;
- FProvider := nil;
- end;
- end;
- FDSBase := nil;
- end;
-
- procedure TClientDataSet.InternalInitFieldDefs;
- var
- I: Integer;
- FieldDescs: PFieldDescList;
- CursorProps: DSProps;
- begin
- Check(FDSBase.GetProps(CursorProps));
- FieldDescs := AllocMem(CursorProps.iFields * SizeOf(DSFLDDesc));
- try
- Check(FDSBase.GetFieldDescs(PDSFldDesc(FieldDescs)));
- FieldDefs.Clear;
- for I := 0 to CursorProps.iFields - 1 do
- AddFieldDesc(FieldDescs^[I], False, I + 1);
- finally
- FreeMem(FieldDescs, CursorProps.iFields * SizeOf(DSFLDDesc));
- end;
- end;
-
- procedure TClientDataSet.InternalOpen;
- var
- CursorProps: DSProps;
- begin
- if not DefaultFields then SetupInternalCalcFields;
- if Assigned(FCloneSource) then
- FDSCursor := CreateDSCursor(FCloneSource.FDSCursor) else
- FDSCursor := CreateDSCursor(nil);
- Check(FDSCursor.GetCursorProps(CursorProps));
- FRecordSize := CursorProps.iRecBufSize;
- BookmarkSize := CursorProps.iBookmarkSize;
- FCanModify := not CursorProps.bReadOnly;
- InternalInitFieldDefs;
- GetIndexInfo;
- if DefaultFields then CreateFields;
- BindFields(True);
- InitBufferPointers(False);
- AllocKeyBuffers;
- if InternalCalcFields then
- Check(FDSBase.SetFieldCalculation(Integer(Self),
- @TClientDataSet.CalcFieldsCallback));
- FDSCursor.MoveToBOF;
- if not Assigned(FCloneSource) then
- begin
- if FIndexName <> '' then
- if FFieldsIndex then
- SortOnFields(FDSCursor, FIndexName, False, False) else
- Check(FDSCursor.UseIndexOrder(PChar(FIndexName)));
- CheckMasterRange;
- if Filtered then ActivateFilters;
- if FReadOnly then FDSBase.SetProp(dspropREADONLY, Integer(True));
- end;
- end;
-
- procedure TClientDataSet.InternalClose;
- begin
- if Filtered then DeactivateFilters;
- FreeKeyBuffers;
- BindFields(False);
- if DefaultFields then DestroyFields;
- FIndexFieldCount := 0;
- FKeySize := 0;
- FDSCursor := nil;
- FLookupCursor := nil;
- FFindCursor := nil;
- end;
-
- function TClientDataSet.IsCursorOpen: Boolean;
- begin
- Result := FDSCursor <> nil;
- end;
-
- procedure TClientDataSet.InternalHandleException;
- begin
- Application.HandleException(Self)
- end;
-
- procedure TClientDataSet.SetData(Value: OleVariant);
- begin
- Close;
- if VarIsNull(Value) then FData := System.NULL else
- begin
- CheckDataPacket(Value);
- FData := Value;
- Open;
- end;
- end;
-
- function TClientDataSet.GetData: OleVariant;
- var
- DataPacket: PVarArray;
- begin
- CheckBrowseMode;
- Check(FDSBase.StreamDS(DataPacket));
- Result := SafeArrayToVariant(DataPacket);
- end;
-
- procedure TClientDataSet.FetchMoreData(All: Boolean);
- var
- Count: Integer;
- RecsOut: Integer;
- begin
- if All then Count := -1 else Count := FPacketRecords;
- AddDataPacket(Provider.GetRecords(Count, RecsOut), RecsOut <> Count);
- FProviderEOF := RecsOut <> Count;
- end;
-
- procedure TClientDataSet.CheckProviderEOF;
- begin
- if not FProviderEOF and FFetchOnDemand then FetchMoreData(True);
- end;
-
- procedure TClientDataSet.AddDataPacket(Data: OleVariant; HitEOF: Boolean);
- begin
- if not VarIsNull(Data) then CheckDataPacket(Data);
- Check(FDSBase.AppendData(TVarData(Data).VArray, HitEOF));
- end;
-
- procedure TClientDataSet.AppendData(Data: OleVariant; HitEOF: Boolean);
- begin
- CheckBrowseMode;
- UpdateCursorPos;
- AddDataPacket(Data, HitEOF);
- Resync([]);
- end;
-
- function TClientDataSet.GetNextPacket: Integer;
- begin
- if FProviderEOF then Result := 0 else
- begin
- AddDataPacket(Provider.GetRecords(FPacketRecords, Result),
- Result <> FPacketRecords);
- FProviderEOF := Result <> FPacketRecords
- end;
- end;
-
- procedure TClientDataSet.SetProviderName(const Value: string);
- begin
- CheckInactive;
- FProvider := nil;
- FData := NULL;
- FProviderName := Value;
- end;
-
- function TClientDataSet.GetProvider: IProvider;
- begin
- if not Assigned(FProvider) then
- begin
- if Assigned(RemoteServer) and (ProviderName <> '') then
- FProvider := RemoteServer.GetProvider(ProviderName);
- if not Assigned(FProvider) then DatabaseError(SNoDataProvider);
- end;
- Result := FProvider;
- end;
-
- procedure TClientDataSet.SetProvider(Value: IProvider);
- begin
- FProvider := Value;
- end;
-
- procedure TClientDataSet.SetRemoteServer(Value: TCustomRemoteServer);
- begin
- if Value = FRemoteServer then Exit;
- CheckInactive;
- FProvider := nil;
- FData := NULL;
- if Assigned(FRemoteServer) then FRemoteServer.RemoveDataSet(Self);
- if Assigned(Value) then Value.AddDataSet(Self);
- FRemoteServer := Value;
- end;
-
- procedure TClientDataSet.Notification(AComponent: TComponent; Operation: TOperation);
- begin
- inherited Notification(AComponent, Operation);
- if (Operation = opRemove) and (FRemoteServer <> nil) and
- (AComponent = FRemoteServer) then
- begin
- FRemoteServer := nil;
- end;
- end;
-
- function TClientDataSet.GetDelta: OleVariant;
- var
- FDeltaDS: IDSBase;
- DataPacket: PVarArray;
- begin
- CheckBrowseMode;
- Check(FDSBase.GetDelta(FDeltaDS));
- Check(FDeltaDS.StreamDS(DataPacket));
- FDelta := SafeArrayToVariant(DataPacket);
- Result := FDelta;
- end;
-
- function TClientDataSet.ApplyUpdates(MaxErrors: Integer): Integer;
- begin
- if ChangeCount = 0 then Result := 0 else
- begin
- Reconcile(Provider.ApplyUpdates(Delta, MaxErrors, Result));
- end;
- end;
-
- procedure TClientDataSet.ClearChangeLog;
- begin
- CheckBrowseMode;
- FDSBase.AcceptChanges;
- end;
-
- procedure TClientDataSet.SetAltRecBuffers(Old, New, Cur: PChar);
- begin
- FOldValueBuffer := Old;
- FNewValueBuffer := New;
- FCurValueBuffer := Cur;
- end;
-
- function TClientDataSet.ReconcileCallback(
- iRslt : Integer; { Previous error if any }
- iUpdateKind : DSAttr; { Update request Insert/Modify/Delete }
- iResAction : dsCBRType; { Resolver response }
- iErrCode : Integer; { Native error-code, (BDE or ..) }
- pErrMessage, { Native errormessage, if any (otherwise NULL) }
- pErrContext : PChar; { 1-level error context, if any (otherwise NULL) }
- pRecUpd, { Record that failed update }
- pRecOrg, { Original record, if any }
- pRecConflict : Pointer { Conflicting error, if any }
- ): dsCBRType;
- var
- Action: TReconcileAction;
- UpdateKind: TUpdateKind;
- begin
- FInReconcileCallback := True;
- try
- SetAltRecBuffers(pRecOrg, pRecUpd, pRecConflict);
- if iUpdateKind = dsRecDeleted then
- UpdateKind := ukDelete
- else if iUpdateKind = dsRecNew then
- UpdateKind := ukInsert
- else
- UpdateKind := ukModify;
- if iResAction = dscbrSkip then
- Action := raSkip else
- Action := raAbort;
- try
- raise EReconcileError.Create(pErrMessage, pErrContext, iErrCode);
- except
- on E: EReconcileError do
- FOnReconcileError(Self, E, UpdateKind, Action);
- end;
- except
- Application.HandleException(Self);
- Action := raAbort;
- end;
- Result := Ord(Action) + 1;
- FInReconcileCallback := False;
- end;
-
- function TClientDataSet.Reconcile(Results: OleVariant): Boolean;
- var
- RCB: Pointer;
- begin
- if VarIsNull(Results) then ClearChangeLog else
- begin
- CheckDataPacket(Results);
- UpdateCursorPos;
- if Assigned(FOnReconcileError) then
- RCB := @TClientDataSet.ReconcileCallback else
- RCB := nil;
- Check(FDSBase.Reconcile(TVarData(FDelta).VArray, TVarData(Results).VArray,
- Integer(Self), RCB));
- Resync([]);
- end;
- Result := (ChangeCount = 0);
- end;
-
- procedure TClientDataSet.NotifyCallback;
- begin
- if State = dsBrowse then Refresh;
- end;
-
- procedure TClientDataSet.SetNotifyCallback;
- begin
- if not FNotifyCallback then
- begin
- Check(FDSCursor.SetNotifyCallBack(Integer(Self), @TClientDataSet.NotifyCallback));
- FNotifyCallback := True;
- end;
- end;
-
- procedure TClientDataSet.CloneCursor(Source: TClientDataSet; Reset: Boolean);
- begin
- Source.CheckActive;
- Close;
- FDSBase := Source.DSBase;
- Source.UpdateCursorPos;
- if not Reset then
- begin
- FCloneSource := Source;
- Filter := Source.Filter;
- OnFilterRecord := Source.OnFilterRecord;
- FilterOptions := Source.FilterOptions;
- Filtered := Source.Filtered;
- if Source.IndexName <> '' then
- IndexName := Source.IndexName else
- IndexFieldNames := Source.IndexFieldNames;
- MasterSource := Source.MasterSource;
- MasterFields := Source.MasterFields;
- ReadOnly := Source.ReadOnly;
- RemoteServer := Source.RemoteServer;
- ProviderName := Source.ProviderName;
- Provider := Source.Provider;
- end;
- try
- Open;
- finally
- FCloneSource := nil;
- end;
- SetNotifyCallback;
- Source.SetNotifyCallback;
- end;
-
- procedure TClientDataSet.EncodeFieldDesc(var FieldDesc: DSFLDDesc;
- const Name: string; DataType: TFieldType; Size: Word; Calculated: Boolean);
- begin
- with FieldDesc do
- begin
- FillChar(FieldDesc, SizeOf(FieldDesc), #0);
- StrCopy(szName, PChar(Name));
- iFldType := FldTypeMap[DataType];
- iFldSubType := FldSubTypeMap[DataType];
- bCalculated := Calculated;
- case DataType of
- ftString, ftBytes, ftVarBytes, ftBlob, ftMemo, ftGraphic,
- ftFmtMemo, ftParadoxOle, ftDBaseOle, ftTypedBinary:
- iUnits1 := Size;
- ftBCD:
- begin
- iUnits1 := 32;
- iUnits2 := Size;
- end;
- end;
- end;
- end;
-
- procedure TClientDataSet.CreateDataSet;
- var
- I: Integer;
- FieldDescs: pDSFLDDesc;
- IndexDesc: DSIdxDesc;
- begin
- CheckInactive;
- if FieldDefs.Count = 0 then
- for I := 0 to FieldCount - 1 do
- with Fields[I] do
- if FieldKind = fkData then
- FieldDefs.Add(FieldName, DataType, Size, Required);
- FieldDescs := AllocMem(FieldDefs.Count * SizeOf(DSFLDDesc));
- try
- for I := 0 to FieldDefs.Count - 1 do
- with FieldDefs[I] do
- EncodeFieldDesc(PFieldDescList(FieldDescs)^[I], Name, DataType, Size, False);
- FDSBase := CreateDSBase;
- Check(FDSBase.Create(FieldDefs.Count, FieldDescs, PChar(Name)));
- try
- for I := 0 to IndexDefs.Count - 1 do
- begin
- with IndexDefs[I] do
- EncodeIndexDesc(IndexDesc, Name, Fields, Options);
- Check(FDSBase.CreateIndex(IndexDesc));
- end;
- except
- FDSBase := nil;
- raise;
- end;
- Open;
- finally
- FreeMem(FieldDescs, FieldDefs.Count * SizeOf(DSFLDDesc));
- end;
- end;
-
- procedure TClientDataSet.SetupInternalCalcFields;
- var
- I: Integer;
- FieldDesc: DSFLDDesc;
- begin
- for I := 0 to FieldCount - 1 do
- begin
- if Fields[I].FieldKind = fkInternalCalc then
- with Fields[I] do
- begin
- EncodeFieldDesc(FieldDesc, FieldName, DataType, Size, True);
- FDSBase.AddField(@FieldDesc);
- end;
- end;
- end;
-
- procedure TClientDataSet.LoadFromStream(Stream: TStream);
- var
- VarData: Pointer;
- StreamData: OleVariant;
- begin
- with Stream do
- begin
- StreamData := VarArrayCreate([0, Size-1], varByte);
- VarData := VarArrayLock(StreamData);
- try
- Read(VarData^, Size);
- finally
- VarArrayUnlock(StreamData);
- end;
- Data := StreamData;
- end;
- end;
-
- procedure TClientDataSet.LoadFromFile(const FileName: string);
- var
- Stream: TStream;
- begin
- Stream := TFileStream.Create(FileName, fmOpenRead);
- try
- LoadFromStream(Stream);
- finally
- Stream.Free;
- end;
- end;
-
- procedure TClientDataSet.SaveToStream(Stream: TStream);
- var
- DataPtr: Pointer;
- DataPacket: PVarArray;
- Size: Integer;
- begin
- CheckBrowseMode;
- CheckProviderEOF;
- Check(FDSBase.StreamDS(DataPacket));
- SafeArrayGetUBound(PSafeArray(DataPacket), 1, Size);
- SafeArrayAccessData(PSafeArray(DataPacket), DataPtr);
- try
- Stream.Write(DataPtr^, Size + 1);
- finally
- SafeArrayUnAccessData(PSafeArray(DataPacket));
- end;
- end;
-
- procedure TClientDataSet.SaveToFile(const FileName: string);
- var
- Stream: TStream;
- begin
- Stream := TFileStream.Create(FileName, fmCreate);
- try
- SaveToStream(Stream);
- finally
- Stream.Free;
- end;
- end;
-
- procedure TClientDataSet.SetLogChanges(Value: Boolean);
- begin
- CheckBrowseMode;
- Check(FDSBase.SetProp(dspropLOGCHANGES, Integer(Value)));
- end;
-
- function TClientDataSet.GetLogChanges: Boolean;
- var
- LogChanges: Integer;
- begin
- CheckBrowseMode;
- Check(FDSBase.GetProp(dspropLOGCHANGES, LogChanges));
- Result := Boolean(LogChanges);
- end;
-
- function TClientDataSet.GetCanModify: Boolean;
- begin
- Result := FCanModify and not ReadOnly;
- end;
-
- procedure TClientDataSet.DisableConstraints;
- begin
- if FConstDisableCount = 0 then
- Check(FDSBase.SetProp(dspropCONSTRAINTS_DISABLED, Longint(True)));
- Inc(FConstDisableCount);
- end;
-
- procedure TClientDataSet.EnableConstraints;
- begin
- if FConstDisableCount <> 0 then
- begin
- Dec(FConstDisableCount);
- if FConstDisableCount = 0 then
- Check(FDSBase.SetProp(dspropCONSTRAINTS_DISABLED, Longint(False)));
- end;
- end;
-
- { Record Functions }
-
- procedure TClientDataSet.InitBufferPointers(GetProps: Boolean);
- var
- CursorProps: DSProps;
- begin
- if GetProps then
- begin
- Check(FDSCursor.GetCursorProps(CursorProps));
- BookmarkSize := CursorProps.iBookmarkSize;
- FRecordSize := CursorProps.iRecBufSize;
- end;
- FRecInfoOfs := FRecordSize + CalcFieldsSize;
- FBookmarkOfs := FRecInfoOfs + SizeOf(TRecInfo);
- FRecBufSize := FBookmarkOfs + BookmarkSize;
- end;
-
- function TClientDataSet.AllocRecordBuffer: PChar;
- begin
- Result := StrAlloc(FRecBufSize);
- end;
-
- procedure TClientDataSet.FreeRecordBuffer(var Buffer: PChar);
- begin
- StrDispose(Buffer);
- end;
-
- procedure TClientDataSet.InternalInitRecord(Buffer: PChar);
- begin
- Check(FDSCursor.InitRecord(Buffer));
- end;
-
- procedure TClientDataSet.ClearCalcFields(Buffer: PChar);
- begin
- FillChar(Buffer[RecordSize], CalcFieldsSize, 0);
- end;
-
- procedure TClientDataSet.InitRecord(Buffer: PChar);
- begin
- inherited InitRecord(Buffer);
- with PRecInfo(Buffer + FRecInfoOfs)^ do
- begin
- BookMarkFlag := bfInserted;
- RecordNumber := -1;
- Attribute := dsRecNew;
- end;
- end;
-
- function TClientDataSet.GetRecord(Buffer: PChar; GetMode: TGetMode;
- DoCheck: Boolean): TGetResult;
- var
- Status: DBIResult;
- begin
- with FDSCursor do
- begin
- case GetMode of
- gmNext:
- begin
- Status := MoveRelative(1);
- if (Status = DBIERR_EOF) and not FProviderEOF and FFetchOnDemand then
- begin
- MoveRelative(-1);
- FetchMoreData(False);
- Status := MoveRelative(1);
- end;
- end;
- gmPrior: Status := MoveRelative(-1);
- else
- Status := DBIERR_NONE;
- end;
- if Status = DBIERR_NONE then
- Status := GetCurrentRecord(Buffer);
- case Status of
- DBIERR_NONE:
- begin
- with PRecInfo(Buffer + FRecInfoOfs)^ do
- begin
- BookmarkFlag := bfCurrent;
- GetSequenceNumber(RecordNumber);
- GetRecordAttribute(Attribute);
- end;
- GetCalcFields(Buffer);
- Check(GetCurrentBookmark(Buffer + FBookmarkOfs));
- Result := grOK;
- end;
- DBIERR_BOF: Result := grBOF;
- DBIERR_EOF: Result := grEOF;
- else
- Result := grError;
- if DoCheck then Check(Status);
- end;
- end;
- end;
-
- function TClientDataSet.GetCurrentRecord(Buffer: PChar): Boolean;
- begin
- if not IsEmpty and (GetBookmarkFlag(ActiveBuffer) = bfCurrent) then
- begin
- UpdateCursorPos;
- Result := (FDSCursor.GetCurrentRecord(Buffer) = DBIERR_NONE);
- end else
- Result := False;
- end;
-
- function TClientDataSet.GetRecordCount: Integer;
- begin
- CheckActive;
- Check(FDSCursor.GetRecordCount(Result));
- end;
-
- function TClientDataSet.GetRecNo: Integer;
- var
- BufPtr: PChar;
- begin
- CheckActive;
- if State = dsCalcFields then
- BufPtr := CalcBuffer else
- BufPtr := ActiveBuffer;
- Result := PRecInfo(BufPtr + FRecInfoOfs).RecordNumber;
- end;
-
- procedure TClientDataSet.SetRecNo(Value: Integer);
- begin
- CheckBrowseMode;
- Check(FDSCursor.MoveToSeqNo(Value));
- Resync([]);
- end;
-
- function TClientDataSet.GetRecordSize: Word;
- begin
- Result := FRecordSize;
- end;
-
- function TClientDataSet.GetActiveRecBuf(var RecBuf: PChar): Boolean;
- begin
- case State of
- dsBrowse: if IsEmpty then RecBuf := nil else RecBuf := ActiveBuffer;
- dsEdit, dsInsert: RecBuf := ActiveBuffer;
- dsSetKey: RecBuf := PChar(FKeyBuffer) + SizeOf(TKeyBuffer);
- dsCalcFields: RecBuf := CalcBuffer;
- dsFilter: RecBuf := FFilterBuffer;
- dsNewValue: RecBuf := FNewValueBuffer;
- dsOldValue: RecBuf := FOldValueBuffer;
- dsCurValue: RecBuf := FCurValueBuffer;
- dsInActive: RecBuf := nil;
- else
- RecBuf := nil;
- end;
- Result := RecBuf <> nil;
- end;
-
- function TClientDataSet.GetChangeCount: Integer;
- begin
- if Active then
- Check(FDSBase.GetProp(dspropNOOFCHANGES, Result)) else
- Result := 0;
- end;
-
- function TClientDataSet.UpdateStatus: TUpdateStatus;
- var
- BufPtr: PChar;
- Attr: Byte;
- begin
- CheckActive;
- if State = dsCalcFields then
- BufPtr := CalcBuffer else
- BufPtr := ActiveBuffer;
- Attr := PRecInfo(BufPtr + FRecInfoOfs).Attribute;
- if Attr = 0 then
- Result := usUnModified
- else if (Attr and dsRecDeleted) <> 0 then
- Result := usDeleted
- else if (Attr and dsRecNew) <> 0 then
- Result := usInserted
- else
- Result := usModified;
- end;
-
- { Field Related }
-
- procedure TClientDataSet.AddFieldDesc(FieldDesc: DSFLDDesc; Required: Boolean;
- FieldNo: Word);
- var
- DataType: TFieldType;
- Size: Word;
- I: Integer;
- Name: string;
- begin
- with FieldDesc do
- begin
- if bHidden then Exit; { Ignore hidden columns }
- I := 0;
- Name := szName;
- while FieldDefs.IndexOf(Name) >= 0 do
- begin
- Inc(I);
- Name := Format('%s_%d', [szName, I]);
- end;
- if iFldType < MAXLOGFLDTYPES then
- DataType := DataTypeMap[iFldType] else
- DataType := ftUnknown;
- Size := 0;
- case iFldType of
- fldZSTRING:
- Size := iUnits1;
- fldINT16, fldUINT16:
- if iFldLen <> 2 then DataType := ftUnknown;
- fldINT32:
- if iFldSubType = fldstAUTOINC then DataType := ftAutoInc;
- fldFLOAT:
- if iFldSubType = fldstMONEY then DataType := ftCurrency;
- fldBCD:
- Size := Abs(iUnits2);
- fldBYTES, fldVARBYTES:
- Size := iUnits1;
- fldBLOB:
- begin
- Size := iUnits1;
- if (iFldSubType >= fldstMEMO) and (iFldSubType <= fldstTYPEDBINARY) then
- DataType := BlobTypeMap[iFldSubType];
- end;
- end;
- if DataType <> ftUnknown then
- with TFieldDef.Create(FieldDefs, Name, DataType, Size, Required, FieldNo) do
- InternalCalcField := FieldDesc.bCalculated;
- end;
- end;
-
- function TClientDataSet.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
- var
- IsBlank: LongBool;
- RecBuf: PChar;
- begin
- Result := False;
- if GetActiveRecBuf(RecBuf) then
- with Field do
- if FieldKind in [fkData, fkInternalCalc] then
- begin
- Check(FDSCursor.GetField(RecBuf, FieldNo, Buffer, IsBlank));
- Result := not IsBlank;
- end else
- if State in [dsBrowse, dsEdit, dsInsert, dsCalcFields] then
- begin
- Inc(RecBuf, FRecordSize + Offset);
- Result := Boolean(RecBuf[0]);
- if Result and (Buffer <> nil) then
- Move(RecBuf[1], Buffer^, DataSize);
- end;
- end;
-
- function TClientDataSet.GetStateFieldValue(State: TDataSetState; Field: TField): Variant;
- var
- IsBlank: Integer;
- begin
- if (State = dsNewValue) then
- begin
- if (FNewValueBuffer = nil) then
- IsBlank := BLANK_NOTCHANGED else
- Check(FDSCursor.GetField(FNewValueBuffer, Field.FieldNo, nil, LongBool(IsBlank)));
- if IsBlank = BLANK_NOTCHANGED then
- begin
- Result := UnAssigned;
- Exit;
- end;
- end;
- Result := inherited GetStateFieldValue(State, Field);
- end;
-
- procedure TClientDataSet.SetFieldData(Field: TField; Buffer: Pointer);
- var
- RecBuf: PChar;
- begin
- with Field do
- begin
- if not (State in dsWriteModes) then DatabaseError(SNotEditing);
- if (State = dsSetKey) and ((FieldNo < 0) or (FIndexFieldCount > 0) and
- not IsIndexField) then DatabaseErrorFmt(SNotIndexField, [DisplayName]);
- GetActiveRecBuf(RecBuf);
- if FieldKind in [fkData, fkInternalCalc] then
- begin
- if ReadOnly and not (State in [dsSetKey, dsFilter]) then
- DatabaseErrorFmt(SFieldReadOnly, [DisplayName]);
- Validate(Buffer);
- Check(FDSCursor.PutField(RecBuf, FieldNo, Buffer));
- end else
- begin
- Inc(RecBuf, FRecordSize + Offset);
- Boolean(RecBuf[0]) := LongBool(Buffer);
- if Boolean(RecBuf[0]) then Move(Buffer^, RecBuf[1], DataSize);
- end;
- if not (State in [dsCalcFields, dsFilter, dsNewValue]) then
- DataEvent(deFieldChange, Longint(Field));
- end;
- end;
-
- function TClientDataSet.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
- begin
- Result := TDSBlobStream.Create(Field as TBlobField, Mode);
- end;
-
- function TClientDataSet.BCDToCurr(BCD: Pointer; var Curr: Currency): Boolean;
- begin
- Result := FMTBCDToCurr(FMTBCD(BCD^), Curr);
- end;
-
- function TClientDataSet.CurrToBCD(const Curr: Currency; BCD: Pointer; Precision,
- Decimals: Integer): Boolean;
- begin
- Result := CurrToFMTBCD(Curr, FMTBCD(BCD^), Precision, Decimals);
- end;
-
- procedure TClientDataSet.RefreshInternalCalcFields(Buffer: PChar);
- begin
- CalculateFields(Buffer);
- end;
-
- function TClientDataSet.CalcFieldsCallBack(RecBuf: PChar): DBIResult;
- var
- SaveState: TDataSetState;
- begin
- try
- SaveState := SetTempState(dsCalcFields);
- try
- CalculateFields(RecBuf);
- finally
- RestoreState(SaveState);
- end;
- except
- end;
- Result := 0;
- end;
-
- { Navigation / Editing }
-
- procedure TClientDataSet.InternalFirst;
- begin
- Check(FDSCursor.MoveToBOF);
- end;
-
- procedure TClientDataSet.InternalLast;
- begin
- CheckProviderEOF;
- Check(FDSCursor.MoveToEOF);
- end;
-
- procedure TClientDataSet.InternalPost;
- begin
- if State = dsEdit then
- Check(FDSCursor.ModifyRecord(ActiveBuffer)) else
- Check(FDSCursor.InsertRecord(ActiveBuffer));
- end;
-
- procedure TClientDataSet.InternalDelete;
- var
- Result: DBIResult;
- begin
- Result := FDSCursor.DeleteRecord;
- if (Result <> DBIERR_NONE) and (Hi(Result) = ERRCAT_NOTFOUND) then
- Check(Result);
- end;
-
- procedure TClientDataSet.Post;
- begin
- inherited Post;
- if State = dsSetKey then
- PostKeyBuffer(True);
- end;
-
- procedure TClientDataSet.Cancel;
- begin
- inherited Cancel;
- if State = dsSetKey then
- PostKeyBuffer(False);
- end;
-
- procedure TClientDataSet.InternalAddRecord(Buffer: Pointer; Append: Boolean);
- begin
- if Append then FDSCursor.MoveToEOF;
- Check(FDSCursor.InsertRecord(Buffer));
- end;
-
- procedure TClientDataSet.InternalGotoBookmark(Bookmark: TBookmark);
- begin
- Check(FDSCursor.MoveToBookmark(Bookmark));
- end;
-
- procedure TClientDataSet.InternalSetToRecord(Buffer: PChar);
- begin
- InternalGotoBookmark(Buffer + FBookmarkOfs);
- end;
-
- function TClientDataSet.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
- begin
- Result := PRecInfo(Buffer + FRecInfoOfs).BookmarkFlag;
- end;
-
- procedure TClientDataSet.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
- begin
- PRecInfo(Buffer + FRecInfoOfs).BookmarkFlag := Value;
- end;
-
- procedure TClientDataSet.GetBookmarkData(Buffer: PChar; Data: Pointer);
- begin
- Move(Buffer[FBookmarkOfs], Data^, BookmarkSize);
- end;
-
- procedure TClientDataSet.SetBookmarkData(Buffer: PChar; Data: Pointer);
- begin
- Move(Data^, ActiveBuffer[FBookmarkOfs], BookmarkSize);
- end;
-
- function TClientDataSet.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Integer;
- const
- RetCodes: array[Boolean, Boolean] of ShortInt = ((2, -1),(1, 0));
- begin
- { Check for uninitialized bookmarks }
- Result := RetCodes[Bookmark1 = nil, Bookmark2 = nil];
- if Result = 2 then
- begin
- Check(FDSCursor.CompareBookmarks(Bookmark1, Bookmark2, Result));
- if Result = 2 then Result := 0;
- end;
- end;
-
- function TClientDataSet.BookmarkValid(Bookmark: TBookmark): Boolean;
- begin
- { ! I think we need to also call GetCurrentRecord here }
- Result := FDSCursor.MoveToBookmark(Bookmark) = DBIERR_NONE;
- if Result then CursorPosChanged;
- end;
-
- procedure TClientDataSet.SyncCursors(Cursor1, Cursor2: IDSCursor);
- var
- BM: DSBOOKMRK;
- begin
- Cursor2.GetCurrentBookmark(@BM);
- Cursor1.MoveToBookmark(@BM);
- end;
-
- function TClientDataSet.UndoLastChange(FollowChange: Boolean): Boolean;
- begin
- Cancel;
- CheckBrowseMode;
- UpdateCursorPos;
- Result := (FDSCursor.UndoLastChange(FollowChange) = DBIERR_NONE);
- if Result then
- begin
- if FollowChange then CursorPosChanged;
- Resync([]);
- end;
- end;
-
- procedure TClientDataSet.RevertRecord;
- begin
- Cancel;
- CheckBrowseMode;
- UpdateCursorPos;
- Check(FDSCursor.RevertRecord);
- Resync([]);
- end;
-
- procedure TClientDataSet.CancelUpdates;
- begin
- Cancel;
- CheckBrowseMode;
- UpdateCursorPos;
- while FDSCursor.UndoLastChange(False) = DBIERR_NONE do {nothing} ;
- CursorPosChanged;
- Resync([]);
- end;
-
- { Indexes }
-
- procedure TClientDataSet.UpdateIndexDefs;
- type
- PIndexDescList = ^TIndexDescList;
- TIndexDescList = array[1..64] of DSIDXDesc;
- var
- I: Integer;
- CursorProps: DSProps;
- IndexDescs: PIndexDescList;
- Options: TIndexOptions;
- Name, Fields: string;
- begin
- if Active and not IndexDefs.Updated then
- begin
- FieldDefs.Update;
- Check(FDSCursor.GetCursorProps(CursorProps));
- IndexDescs := AllocMem(CursorProps.iIndexes * SizeOf(CursorProps));
- try
- IndexDefs.Clear;
- Check(FDSBase.GetIndexDescs(PDSIDXDesc(IndexDescs)));
- for I := 1 to CursorProps.iIndexes do
- begin
- DecodeIndexDesc(IndexDescs^[I], Name, Fields, Options);
- with IndexDefs do
- Add(Name, Fields, Options);
- end;
- IndexDefs.Updated := True;
- finally
- FreeMem(IndexDescs, CursorProps.iIndexes * SizeOf(CursorProps));
- end;
- end;
- end;
-
- procedure TClientDataSet.DecodeIndexDesc(const IndexDesc: DSIDXDesc;
- var Name, Fields: string; var Options: TIndexOptions);
- var
- I: Integer;
- begin
- with IndexDesc do
- begin
- Name := szName;
- Options := [];
- if bUnique then Include(Options, ixUnique);
- Fields := '';
- for I := 0 to iFields - 1 do
- begin
- if I <> 0 then Fields := Fields + ';';
- Fields := Fields + FieldDefs[iKeyFields[I] - 1].Name;
- end;
- end;
- end;
-
- procedure TClientDataSet.GetIndexNames(List: TStrings);
- var
- I: Integer;
- begin
- UpdateIndexDefs;
- List.BeginUpdate;
- try
- List.Clear;
- for I := 0 to IndexDefs.Count - 1 do
- with IndexDefs[I] do
- if Name <> '' then List.Add(Name);
- finally
- List.EndUpdate;
- end;
- end;
-
- function TClientDataSet.GetIndexDefs: TIndexDefs;
- begin
- if FIndexDefs = nil then
- FIndexDefs := TIndexDefs.Create(Self);
- Result := FIndexDefs;
- end;
-
- procedure TClientDataSet.GetIndexInfo;
- var
- IndexDesc: DSIDXDesc;
- begin
- if FDSCursor.GetIndexDescs(True, IndexDesc) = 0 then
- begin
- FIndexFieldCount := IndexDesc.iFields;
- FIndexFieldMap := IndexDesc.iKeyFields;
- FKeySize := IndexDesc.iKeyLen;
- end;
- end;
-
- procedure TClientDataSet.SwitchToIndex(const IndexName: string);
- begin
- ResetCursorRange;
- Check(FDSCursor.UseIndexOrder(PChar(IndexName)));
- GetIndexInfo;
- end;
-
- procedure TClientDataSet.SetIndex(const Value: string; FieldsIndex: Boolean);
- begin
- if Active then
- begin
- CheckBrowseMode;
- UpdateCursorPos;
- CheckProviderEOF;
- if (FIndexName <> Value) or (FFieldsIndex <> FieldsIndex) then
- begin
- if FieldsIndex then
- SortOnFields(FDSCursor, Value, False, False) else
- SwitchToIndex(Value);
- CheckMasterRange;
- Resync([]);
- end;
- end;
- FIndexName := Value;
- FFieldsIndex := FieldsIndex;
- end;
-
- procedure TClientDataSet.EncodeIndexDesc(var IndexDesc: DSIDXDesc;
- const Name, Fields: string; Options: TIndexOptions);
- var
- Pos: Integer;
- descending,
- CaseInsensitive: Bool;
- begin
- FillChar(IndexDesc, SizeOf(IndexDesc), 0);
- with IndexDesc do
- begin
- bUnique := ixUnique in Options;
- Descending := ixDescending in Options;
- CaseInsensitive := ixCaseInsensitive in Options;
- Pos := 1;
- while (Pos <= Length(Fields)) and (iFields < MAXKEYFIELDS) do
- begin
- iKeyFields[iFields] :=
- FieldDefs.Find(ExtractFieldName(Fields, Pos)).FieldNo;
- bDescending[iFields] := Descending;
- bCaseInsensitive[iFields] := CaseInsensitive;
- Inc(iFields);
- end;
- StrCopy(szName, PChar(Name));
- end;
- end;
-
- procedure TClientDataSet.AddIndex(const Name, Fields: string; Options: TIndexOptions);
- var
- IndexDesc: DSIDXDesc;
- begin
- CheckBrowseMode;
- FieldDefs.Update;
- EncodeIndexDesc(IndexDesc, Name, Fields, Options);
- CursorPosChanged;
- Check(FDSBase.CreateIndex(IndexDesc));
- IndexDefs.Updated := False;
- end;
-
- procedure TClientDataSet.DeleteIndex(const Name: string);
- begin
- CheckBrowseMode;
- if AnsiCompareText(Name, IndexName) = 0 then IndexName := '';
- Check(FDSBase.RemoveIndex(PChar(Name)));
- IndexDefs.Updated := False;
- end;
-
- function TClientDataSet.GetIndexField(Index: Integer): TField;
- var
- FieldNo: Integer;
- begin
- if (Index < 0) or (Index >= FIndexFieldCount) then
- DatabaseError(SFieldIndexError);
- FieldNo := FIndexFieldMap[Index];
- Result := FieldByNumber(FieldNo);
- if Result = nil then
- DatabaseErrorFmt(SIndexFieldMissing, [FieldDefs[FieldNo - 1].Name]);
- end;
-
- function TClientDataSet.GetIsIndexField(Field: TField): Boolean;
- var
- I: Integer;
- begin
- Result := False;
- with Field do
- if FieldNo > 0 then
- for I := 0 to FIndexFieldCount - 1 do
- if FIndexFieldMap[I] = FieldNo then
- begin
- Result := True;
- Exit;
- end;
- end;
-
- function TClientDataSet.GetIndexName: string;
- begin
- if FFieldsIndex then Result := '' else Result := FIndexName;
- end;
-
- procedure TClientDataSet.SetIndexName(const Value: string);
- begin
- SetIndex(Value, False);
- end;
-
- procedure TClientDataSet.SetIndexField(Index: Integer; Value: TField);
- begin
- GetIndexField(Index).Assign(Value);
- end;
-
- function TClientDataSet.GetIndexFieldNames: string;
- begin
- if FFieldsIndex then Result := FIndexName else Result := '';
- end;
-
- procedure TClientDataSet.SetIndexFieldNames(const Value: string);
- begin
- SetIndex(Value, Value <> '');
- end;
-
- function TClientDataSet.GetIndexFieldCount: Integer;
- begin
- Result := FIndexFieldCount;
- end;
-
- procedure TClientDataSet.SortOnFields(Cursor: IDSCursor; const Fields: string;
- CaseInsensitive, Descending: Boolean);
- var
- I: Integer;
- FieldList: TList;
- DescFlags, CaseFlags: DSKEYBOOL;
-
- function GetFlags(Flag: Bool; var FlagArray: DSKEYBOOL): Pointer;
- var
- J: Integer;
- begin
- if not Flag then Result := nil else
- begin
- for J := 0 to FieldList.Count - 1 do
- FlagArray[J] := True;
- Result := @FlagArray;
- end;
- end;
-
- begin
- FieldList := TList.Create;
- try
- GetFieldList(FieldList, Fields);
- for I := 0 to FieldList.Count - 1 do
- if TField(FieldList[I]).FieldNo > 0 then
- FieldList[I] := Pointer(TField(FieldList[I]).FieldNo) else
- DatabaseError(SFieldIndexError); { ! Need Better Error here for calc field }
- Check(Cursor.SortOnFields(FieldList.Count, PInteger(FieldList.List),
- GetFlags(Descending, DescFlags), GetFlags(CaseInsensitive, CaseFlags)));
- GetIndexInfo;
- finally
- FieldList.Free;
- end;
- end;
-
- { Ranges / Keys }
-
- procedure TClientDataSet.AllocKeyBuffers;
- var
- KeyIndex: TKeyIndex;
- begin
- try
- for KeyIndex := Low(TKeyIndex) to High(TKeyIndex) do
- FKeyBuffers[KeyIndex] := InitKeyBuffer(
- AllocMem(SizeOf(TKeyBuffer) + FRecordSize));
- if Assigned(FCloneSource) then
- for KeyIndex := Low(TKeyIndex) to High(TKeyIndex) do
- Move(FCloneSource.FKeyBuffers[KeyIndex]^, FKeyBuffers[KeyIndex]^,
- SizeOf(TKeyBuffer) + FRecordSize);
- except
- FreeKeyBuffers;
- raise;
- end;
- end;
-
- procedure TClientDataSet.FreeKeyBuffers;
- var
- KeyIndex: TKeyIndex;
- begin
- for KeyIndex := Low(TKeyIndex) to High(TKeyIndex) do
- DisposeMem(FKeyBuffers[KeyIndex], SizeOf(TKeyBuffer) + FRecordSize);
- end;
-
- function TClientDataSet.InitKeyBuffer(Buffer: PKeyBuffer): PKeyBuffer;
- begin
- FillChar(Buffer^, SizeOf(TKeyBuffer) + FRecordSize, 0);
- Check(FDSCursor.InitRecord(PChar(Buffer) + SizeOf(TKeyBuffer)));
- Result := Buffer;
- end;
-
- procedure TClientDataSet.CheckSetKeyMode;
- begin
- if State <> dsSetKey then DatabaseError(SNotEditing);
- end;
-
- function TClientDataSet.SetCursorRange: Boolean;
- var
- RangeStart, RangeEnd: PKeyBuffer;
- StartKey, EndKey: PChar;
- begin
- Result := False;
- if not (
- BuffersEqual(FKeyBuffers[kiRangeStart], FKeyBuffers[kiCurRangeStart],
- SizeOf(TKeyBuffer) + FRecordSize) and
- BuffersEqual(FKeyBuffers[kiRangeEnd], FKeyBuffers[kiCurRangeEnd],
- SizeOf(TKeyBuffer) + FRecordSize)) then
- begin
- CheckProviderEOF;
- RangeStart := FKeyBuffers[kiRangeStart];
- RangeEnd := FKeyBuffers[kiRangeEnd];
- StartKey := PChar(RangeStart) + SizeOf(TKeyBuffer);
- EndKey := PChar(RangeEnd) + SizeOf(TKeyBuffer);
- Check(FDSCursor.SetRange(RangeStart.FieldCount, StartKey,
- not RangeStart.Exclusive, EndKey, not RangeEnd.Exclusive));
- Move(FKeyBuffers[kiRangeStart]^, FKeyBuffers[kiCurRangeStart]^,
- SizeOf(TKeyBuffer) + FRecordSize);
- Move(FKeyBuffers[kiRangeEnd]^, FKeyBuffers[kiCurRangeEnd]^,
- SizeOf(TKeyBuffer) + FRecordSize);
- DestroyLookupCursor;
- Result := True;
- end;
- end;
-
- function TClientDataSet.ResetCursorRange: Boolean;
- begin
- Result := False;
- if FKeyBuffers[kiCurRangeStart].Modified or
- FKeyBuffers[kiCurRangeEnd].Modified then
- begin
- Check(FDSCursor.DropRange);
- InitKeyBuffer(FKeyBuffers[kiCurRangeStart]);
- InitKeyBuffer(FKeyBuffers[kiCurRangeEnd]);
- DestroyLookupCursor;
- Result := True;
- end;
- end;
-
- procedure TClientDataSet.SetLinkRanges(MasterFields: TList);
- var
- I: Integer;
- SaveState: TDataSetState;
- begin
- SaveState := SetTempState(dsSetKey);
- try
- FKeyBuffer := InitKeyBuffer(FKeyBuffers[kiRangeStart]);
- FKeyBuffer^.Modified := True;
- for I := 0 to MasterFields.Count - 1 do
- GetIndexField(I).Assign(TField(MasterFields[I]));
- FKeyBuffer^.FieldCount := MasterFields.Count;
- finally
- RestoreState(SaveState);
- end;
- Move(FKeyBuffers[kiRangeStart]^, FKeyBuffers[kiRangeEnd]^,
- SizeOf(TKeyBuffer) + FRecordSize);
- end;
-
- function TClientDataSet.GetKeyBuffer(KeyIndex: TKeyIndex): PKeyBuffer;
- begin
- Result := FKeyBuffers[KeyIndex];
- end;
-
- procedure TClientDataSet.SetKeyBuffer(KeyIndex: TKeyIndex; Clear: Boolean);
- begin
- CheckBrowseMode;
- FKeyBuffer := FKeyBuffers[KeyIndex];
- Move(FKeyBuffer^, FKeyBuffers[kiSave]^, SizeOf(TKeyBuffer) + FRecordSize);
- if Clear then InitKeyBuffer(FKeyBuffer);
- SetState(dsSetKey);
- SetModified(FKeyBuffer.Modified);
- DataEvent(deDataSetChange, 0);
- end;
-
- procedure TClientDataSet.PostKeyBuffer(Commit: Boolean);
- begin
- DataEvent(deCheckBrowseMode, 0);
- if Commit then
- FKeyBuffer.Modified := Modified else
- Move(FKeyBuffers[kiSave]^, FKeyBuffer^, SizeOf(TKeyBuffer) + FRecordSize);
- SetState(dsBrowse);
- DataEvent(deDataSetChange, 0);
- end;
-
- function TClientDataSet.GetKeyExclusive: Boolean;
- begin
- CheckSetKeyMode;
- Result := FKeyBuffer.Exclusive;
- end;
-
- procedure TClientDataSet.SetKeyExclusive(Value: Boolean);
- begin
- CheckSetKeyMode;
- FKeyBuffer.Exclusive := Value;
- end;
-
- function TClientDataSet.GetKeyFieldCount: Integer;
- begin
- CheckSetKeyMode;
- Result := FKeyBuffer.FieldCount;
- end;
-
- procedure TClientDataSet.SetKeyFieldCount(Value: Integer);
- begin
- CheckSetKeyMode;
- FKeyBuffer.FieldCount := Value;
- end;
-
- procedure TClientDataSet.SetKeyFields(KeyIndex: TKeyIndex;
- const Values: array of const);
- var
- I: Integer;
- SaveState: TDataSetState;
- begin
- if FIndexFieldCount = 0 then DatabaseError(SNoFieldIndexes);
- SaveState := SetTempState(dsSetKey);
- try
- FKeyBuffer := InitKeyBuffer(FKeyBuffers[KeyIndex]);
- for I := 0 to High(Values) do GetIndexField(I).AssignValue(Values[I]);
- FKeyBuffer^.FieldCount := High(Values) + 1;
- FKeyBuffer^.Modified := Modified;
- finally
- RestoreState(SaveState);
- end;
- end;
-
- function TClientDataSet.FindKey(const KeyValues: array of const): Boolean;
- begin
- CheckBrowseMode;
- SetKeyFields(kiLookup, KeyValues);
- Result := GotoKey;
- end;
-
- procedure TClientDataSet.FindNearest(const KeyValues: array of const);
- begin
- CheckBrowseMode;
- SetKeyFields(kiLookup, KeyValues);
- GotoNearest
- end;
-
- function TClientDataSet.GotoKey: Boolean;
- var
- KeyBuffer: PKeyBuffer;
- RecBuffer: PChar;
- begin
- CheckBrowseMode;
- CursorPosChanged;
- CheckProviderEOF;
- KeyBuffer := GetKeyBuffer(kiLookup);
- RecBuffer := PChar(KeyBuffer) + SizeOf(TKeyBuffer);
- Result := FDSCursor.GetRecordForKey(KeyBuffer.FieldCount, 0, RecBuffer, nil) = 0;
- if Result then Resync([rmExact, rmCenter]);
- end;
-
- procedure TClientDataSet.GotoNearest;
- var
- SearchCond: DBISearchCond;
- KeyBuffer: PKeyBuffer;
- RecBuffer: PChar;
- begin
- CheckBrowseMode;
- CheckProviderEOF;
- KeyBuffer := GetKeyBuffer(kiLookup);
- RecBuffer := PChar(KeyBuffer) + SizeOf(TKeyBuffer);
- if KeyBuffer^.Exclusive then
- SearchCond := keySEARCHGT else
- SearchCond := keySEARCHGEQ;
- Check(FDSCursor.MoveToKey(SearchCond, KeyBuffer.FieldCount, 0, RecBuffer));
- Resync([rmCenter]);
- end;
-
- procedure TClientDataSet.SetKey;
- begin
- SetKeyBuffer(kiLookup, True);
- end;
-
- procedure TClientDataSet.EditKey;
- begin
- SetKeyBuffer(kiLookup, False);
- end;
-
- procedure TClientDataSet.ApplyRange;
- begin
- CheckBrowseMode;
- if SetCursorRange then First;
- end;
-
- procedure TClientDataSet.CancelRange;
- begin
- CheckBrowseMode;
- UpdateCursorPos;
- if ResetCursorRange then Resync([]);
- end;
-
- procedure TClientDataSet.SetRange(const StartValues, EndValues: array of const);
- begin
- CheckBrowseMode;
- SetKeyFields(kiRangeStart, StartValues);
- SetKeyFields(kiRangeEnd, EndValues);
- ApplyRange;
- end;
-
- procedure TClientDataSet.SetRangeEnd;
- begin
- SetKeyBuffer(kiRangeEnd, True);
- end;
-
- procedure TClientDataSet.SetRangeStart;
- begin
- SetKeyBuffer(kiRangeStart, True);
- end;
-
- procedure TClientDataSet.EditRangeEnd;
- begin
- SetKeyBuffer(kiRangeEnd, False);
- end;
-
- procedure TClientDataSet.EditRangeStart;
- begin
- SetKeyBuffer(kiRangeStart, False);
- end;
-
- { Master / Detail }
-
- procedure TClientDataSet.CheckMasterRange;
- begin
- if FMasterLink.Active and (FMasterLink.Fields.Count > 0) then
- begin
- SetLinkRanges(FMasterLink.Fields);
- SetCursorRange;
- end;
- end;
-
- procedure TClientDataSet.MasterChanged(Sender: TObject);
- begin
- CheckBrowseMode;
- SetLinkRanges(FMasterLink.Fields);
- ApplyRange;
- end;
-
- procedure TClientDataSet.MasterDisabled(Sender: TObject);
- begin
- CancelRange;
- end;
-
- procedure TClientDataSet.Loaded;
- begin
- inherited Loaded;
- if not VarIsNull(FData) then Open;
- end;
-
- procedure TClientDataSet.ReadData(Stream: TStream);
- var
- Size: Integer;
- FDataPtr: Pointer;
- begin
- Stream.ReadBuffer(Size, SizeOf(Size));
- if Size > 0 then
- begin
- FData := VarArrayCreate([0, Size-1], varByte);
- try
- FDataPtr := VarArrayLock(FData);
- try
- Stream.ReadBuffer(FDataPtr^, Size);
- finally
- VarArrayUnlock(FData);
- end;
- except
- FData := System.NULL;
- raise;
- end;
- end else
- FData := System.NULL;
- end;
-
- procedure TClientDataSet.WriteData(Stream: TStream);
- var
- P: Pointer;
- Size: Integer;
- begin
- CheckBrowseMode;
- P := VarArrayLock(FData);
- try
- Size := VarArrayHighBound(FData, 1);
- Stream.WriteBuffer(Size, SizeOf(Size));
- Stream.WriteBuffer(P^, Size);
- finally
- VarArrayUnlock(FData);
- end;
- end;
-
- function TClientDataSet.GetDataSource: TDataSource;
- begin
- Result := FMasterLink.DataSource;
- end;
-
- procedure TClientDataSet.SetDataSource(Value: TDataSource);
- begin
- if IsLinkedTo(Value) then DatabaseError(SCircularDataLink);
- FMasterLink.DataSource := Value;
- end;
-
- function TClientDataSet.GetMasterFields: string;
- begin
- Result := FMasterLink.FieldNames;
- end;
-
- procedure TClientDataSet.SetMasterFields(const Value: string);
- begin
- FMasterLink.FieldNames := Value;
- end;
-
- procedure TClientDataSet.DoOnNewRecord;
- var
- I: Integer;
- begin
- if FMasterLink.Active and (FMasterLink.Fields.Count > 0) then
- for I := 0 to FMasterLink.Fields.Count - 1 do
- IndexFields[I] := TField(FMasterLink.Fields[I]);
- inherited DoOnNewRecord;
- end;
-
- procedure TClientDataSet.DefineProperties(Filer: TFiler);
- begin
- inherited DefineProperties(Filer);
- Filer.DefineBinaryProperty('Data', ReadData, WriteData, ((State = dsBrowse) and
- not VarIsNull(FData) and not Assigned(FProvider)));
- end;
-
- { Filters }
-
- procedure TClientDataSet.ActivateFilters;
- begin
- if Filter <> '' then
- AddExprFilter(Filter, FilterOptions);
- if Assigned(OnFilterRecord) then
- AddFuncFilter;
- end;
-
- procedure TClientDataSet.DeactivateFilters;
- begin
- if FFuncFilter <> nil then
- begin
- FDSCursor.DropFilter(FFuncFilter);
- FFuncFilter := nil;
- end;
- if FExprFilter <> nil then
- begin
- FDSCursor.DropFilter(FExprFilter);
- FExprFilter := nil;
- end;
- end;
-
- procedure TClientDataSet.AddExprFilter(const Expr: string; Options: TFilterOptions);
- begin
- if FExprFilter <> nil then FDSCursor.DropFilter(FExprFilter);
- if Expr <> '' then
- with TExprParser.Create(Self, Expr, Options) do
- try
- CheckProviderEOF;
- Check(FDSCursor.AddFilter(FilterData, DataSize, FExprFilter));
- finally
- Free;
- end;
- end;
-
- function TClientDataSet.FilterCallback(RecBuf: PChar): Bool;
- var
- SaveState: TDataSetState;
- Accept: Boolean;
- begin
- SaveState := SetTempState(dsFilter);
- FFilterBuffer := RecBuf;
- try
- Accept := True;
- OnFilterRecord(Self, Accept);
- except
- Application.HandleException(Self);
- end;
- RestoreState(SaveState);
- Result := Accept;
- end;
-
- procedure TClientDataSet.AddFuncFilter;
- begin
- if FFuncFilter <> nil then FDSCursor.DropFilter(FFuncFilter);
- CheckProviderEOF;
- Check(FDSCursor.AddFilterCallBack(Integer(Self), @TClientDataSet.FilterCallback,
- FFuncFilter));
- end;
-
- procedure TClientDataSet.SetFilterData(const Text: string; Options: TFilterOptions);
- begin
- if Active and Filtered then
- begin
- CheckBrowseMode;
- if (Filter <> Text) or (FilterOptions <> Options) then
- AddExprFilter(Text, Options);
- DestroyLookupCursor;
- First;
- end;
- inherited SetFilterText(Text);
- inherited SetFilterOptions(Options);
- end;
-
- procedure TClientDataSet.SetFilterText(const Value: string);
- begin
- SetFilterData(Value, FilterOptions);
- end;
-
- procedure TClientDataSet.SetFilterOptions(Value: TFilterOptions);
- begin
- SetFilterData(Filter, Value);
- end;
-
- procedure TClientDataSet.SetOnFilterRecord(const Value: TFilterRecordEvent);
- begin
- if Active and Filtered then
- begin
- CheckBrowseMode;
- if Assigned(OnFilterRecord) <> Assigned(Value) then
- begin
- if Assigned(Value) then
- begin
- inherited SetOnFilterRecord(Value);
- AddFuncFilter;
- end else
- FDSCursor.DropFilter(FFuncFilter);
- end;
- DestroyLookupCursor;
- First;
- end;
- inherited SetOnFilterRecord(Value);
- end;
-
- procedure TClientDataSet.SetFiltered(Value: Boolean);
- begin
- if Active then
- begin
- CheckBrowseMode;
- if Filtered <> Value then
- begin
- DestroyLookupCursor;
- FDSCursor.MoveToBOF;
- if Value then ActivateFilters else DeactivateFilters;
- inherited SetFiltered(Value);
- end;
- First;
- end else
- inherited SetFiltered(Value);
- end;
-
- function TClientDataSet.FindRecord(Restart, GoForward: Boolean): Boolean;
- var
- Status: DBIResult;
- Cursor: IDSCursor;
- begin
- CheckBrowseMode;
- SetFound(False);
- UpdateCursorPos;
- CursorPosChanged;
- CheckProviderEOF;
- DoBeforeScroll;
- if not Filtered then
- begin
- if not Assigned(FFindCursor) then
- begin
- ActivateFilters;
- try
- FFindCursor := CreateDSCursor(FDSCursor)
- finally
- DeactivateFilters;
- end;
- end else
- if not Restart then SyncCursors(FFindCursor, FDSCursor);
- Cursor := FFindCursor;
- end else
- Cursor := FDSCursor;
- if GoForward then
- begin
- if Restart then Check(Cursor.MoveToBOF);
- Status := Cursor.MoveRelative(1);
- end else
- begin
- if Restart then Check(Cursor.MoveToEOF);
- Status := Cursor.MoveRelative(-1);
- end;
- if Cursor <> FDSCursor then
- SyncCursors(FDSCursor, FFindCursor);
- if Status = DBIERR_NONE then
- begin
- Resync([rmExact, rmCenter]);
- SetFound(True);
- end;
- Result := Found;
- if Result then DoAfterScroll;
- end;
-
- procedure TClientDataSet.DestroyLookupCursor;
- begin
- FLookupCursor := nil;
- FFindCursor := nil;
- end;
-
- function TClientDataSet.LocateRecord(const KeyFields: string;
- const KeyValues: Variant; Options: TLocateOptions;
- SyncCursor: Boolean): Boolean;
- var
- Fields: TList;
- Buffer: PChar;
- I, FieldCount, PartialLength: Integer;
- Status: DBIResult;
- CaseInsensitive: Boolean;
- begin
- CheckBrowseMode;
- CursorPosChanged;
- CheckProviderEOF;
- Buffer := TempBuffer;
- Fields := TList.Create;
- try
- GetFieldList(Fields, KeyFields);
- CaseInsensitive := loCaseInsensitive in Options;
- if not Assigned(FLookupCursor) then
- FLookupCursor := CreateDSCursor(FDSCursor);
- SortOnFields(FLookupCursor, KeyFields, CaseInsensitive, False);
- FFilterBuffer := Buffer;
- SetTempState(dsFilter);
- try
- InitRecord(Buffer);
- FieldCount := Fields.Count;
- if FieldCount = 1 then
- TField(Fields.First).Value := KeyValues
- else
- for I := 0 to FieldCount - 1 do
- TField(Fields[I]).Value := KeyValues[I];
- PartialLength := 0;
- if (loPartialKey in Options) and
- (TField(Fields.Last).DataType = ftString) then
- begin
- Dec(FieldCount);
- PartialLength := Length(TField(Fields.Last).AsString);
- end;
- Status := FLookupCursor.GetRecordForKey(FieldCount, PartialLength, Buffer, Buffer);
- finally
- RestoreState(dsBrowse);
- end;
- if SyncCursor and (Status = DBIERR_NONE) then
- SyncCursors(FDSCursor, FLookupCursor);
- finally
- Fields.Free;
- end;
- Result := Status = DBIERR_NONE;
- end;
-
- function TClientDataSet.Lookup(const KeyFields: string; const KeyValues: Variant;
- const ResultFields: string): Variant;
- begin
- Result := Null;
- if LocateRecord(KeyFields, KeyValues, [], False) then
- begin
- SetTempState(dsCalcFields);
- try
- CalculateFields(TempBuffer);
- Result := FieldValues[ResultFields];
- finally
- RestoreState(dsBrowse);
- end;
- end;
- end;
-
- function TClientDataSet.Locate(const KeyFields: string;
- const KeyValues: Variant; Options: TLocateOptions): Boolean;
- begin
- DoBeforeScroll;
- Result := LocateRecord(KeyFields, KeyValues, Options, True);
- if Result then
- begin
- Resync([rmExact, rmCenter]);
- DoAfterScroll;
- end;
- end;
-
- procedure TClientDataSet.GotoCurrent(DataSet: TClientDataSet);
- begin
- CheckBrowseMode;
- CheckProviderEOF;
- DataSet.CheckActive;
- BookMark := DataSet.BookMark;
- end;
-
- end.
-